#include "App.h"
App *				g_pApp		= 0;
IDirect3DDevice9*	g_pD3dDev	= 0;

LRESULT CALLBACK MainWndProc(HWND p_hWnd, UINT p_uMsg, WPARAM p_wParam, LPARAM p_lParam)
{
	if(g_pApp)
	{
		return g_pApp->msgProc(p_uMsg, p_wParam, p_lParam);
	}
	else
	{
		return DefWindowProc(p_hWnd, p_uMsg, p_wParam, p_lParam);
	}
}

App::App(HINSTANCE p_hInstance, LPTSTR p_sName, D3DDEVTYPE p_devType, DWORD p_requestedVP)
{
	m_hInstance = p_hInstance;
	m_sName = p_sName;
	m_devType = p_devType;
	m_requestedVP = p_requestedVP;
	m_pD3dObject = 0;
	ZeroMemory(&m_d3dPP, sizeof(m_d3dPP));
	initWindow();
	initDirectX();
	initState();
}
App::~App()
{

}
void App::initWindow()
{
	WNDCLASSEX wc;
	wc.style			= CS_HREDRAW | CS_VREDRAW;
	wc.lpfnWndProc		= MainWndProc; 
	wc.cbClsExtra		= 0;
	wc.cbWndExtra		= 0;
	wc.cbSize			= sizeof(wc);
	wc.hInstance		= m_hInstance;
	wc.hIcon			= LoadIcon(0, IDI_APPLICATION);
	wc.hCursor			= LoadCursor(0, IDC_ARROW);
	wc.hbrBackground	= (HBRUSH)GetStockObject(WHITE_BRUSH);
	wc.lpszMenuName		= 0;
	wc.lpszClassName	= __TEXT("VCityWindow");
	wc.hIconSm			= 0;
	
	if(!RegisterClassEx(&wc))
	{
		MessageBox(0, __TEXT("RegisterClass failed!"), __TEXT("Error"), MB_OK);
		PostQuitMessage(0);
		return;
	}
	RECT rect = {0, 0, 600, 400};
	AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, false);
	m_hWnd = CreateWindow
	(
		wc.lpszClassName, 
		m_sName, 
		WS_OVERLAPPEDWINDOW, 
		(GetSystemMetrics(SM_CXSCREEN) - rect.right + rect.left)/2, 
		(GetSystemMetrics(SM_CYSCREEN) - rect.bottom + rect.top)/2, 
		rect.right, 
		rect.bottom,
		0, 
		0, 
		m_hInstance, 
		0
	); 
	if(!m_hWnd)
	{
		MessageBox(0, L"CreateWindow FAILED", 0, 0);
		PostQuitMessage(0);
	}
	ShowWindow(m_hWnd, SW_SHOW);
	UpdateWindow(m_hWnd);
}
void App::initDirectX()
{
	m_pD3dObject = Direct3DCreate9(D3D_SDK_VERSION);
	if(m_pD3dObject)
	{
		m_d3dPP.Windowed			= true;
		m_d3dPP.SwapEffect			= D3DSWAPEFFECT_DISCARD;
		m_d3dPP.BackBufferFormat	= D3DFMT_UNKNOWN;

		HR
		(
			m_pD3dObject->CreateDevice
			(
				D3DADAPTER_DEFAULT, 
				m_devType,          
				m_hWnd,          
				m_requestedVP,
				&m_d3dPP,         
				&g_pD3dDev
			)
		);
	}
	else
	{
		MessageBox(0, L"Create Direct3D Failed!", 0, 0);
		PostQuitMessage(0);
	}
	DirectInput di(DISCL_NONEXCLUSIVE | DISCL_FOREGROUND, DISCL_NONEXCLUSIVE | DISCL_FOREGROUND);
	g_pInput = &di;
}
void App::initState()
{
	m_pCurrentState = new State();
}
LRESULT App::msgProc(UINT p_uMsg, WPARAM p_wParam, LPARAM p_lParam)
{
	switch(p_uMsg)
	{
	case WM_EXITSIZEMOVE:
		{
			RECT rect;
			GetClientRect(m_hWnd, &rect);
			m_d3dPP.BackBufferWidth = rect.right - rect.left;
			m_d3dPP.BackBufferHeight = rect.bottom - rect.top;
			m_pCurrentState->onLostDevice();
			HR(g_pD3dDev->Reset(&m_d3dPP));
			m_pCurrentState->onResetDevice();
			return 0;
		}
	case WM_CLOSE:
		{
			DestroyWindow(m_hWnd);
			return 0;
		}
	case WM_DESTROY:
		{
			PostQuitMessage(0);
			return 0;
		}
	}
	return DefWindowProc(m_hWnd, p_uMsg, p_wParam, p_lParam);
}

LPTSTR App::getName()
{
	return m_sName;
}
HINSTANCE  App::getInstance()
{
	return m_hInstance;
}
HWND App::getWnd()
{
	return m_hWnd;
}

bool App::checkDevCaps()
{

	return true;
}
bool App::isDevReady()
{
	switch(g_pD3dDev->TestCooperativeLevel())
	{
	case D3DERR_DEVICENOTRESET:
		m_pCurrentState->onLostDevice();
		HR(g_pD3dDev->Reset(&m_d3dPP));
		m_pCurrentState->onResetDevice();
		return true;
	case D3DERR_DEVICELOST:
		Sleep(50);
		return false;
	case D3DERR_DRIVERINTERNALERROR:
		MessageBox(HWND_TOP, __TEXT("DRIVERINTERNALERROR"), __TEXT("ERROR"), MB_OK);
		PostQuitMessage(0);
		return false;
	default:
		return true;
	}
}
void App::switchScreenMode(bool p_bFullScreen)
{
	if (p_bFullScreen)
	{
		if (!m_d3dPP.Windowed)
		{
			return;
		}
		m_d3dPP.Windowed = false;
		m_d3dPP.BackBufferFormat = D3DFMT_A8R8G8B8;
		m_d3dPP.BackBufferWidth = GetSystemMetrics(SM_CXSCREEN);
		m_d3dPP.BackBufferHeight = GetSystemMetrics(SM_CYSCREEN);

		SetWindowLongPtr(m_hWnd, GWL_STYLE, WS_POPUP);
		SetWindowPos(m_hWnd, HWND_TOP, 0, 0, m_d3dPP.BackBufferWidth, m_d3dPP.BackBufferHeight, SWP_NOZORDER | SWP_SHOWWINDOW);
	}
	else
	{
		if (m_d3dPP.Windowed)
		{
			return;
		}
		RECT rect = {0, 0, 600, 400};
		AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, false);
		m_d3dPP.Windowed = true;
		m_d3dPP.BackBufferFormat = D3DFMT_UNKNOWN;
		m_d3dPP.BackBufferWidth = 600;
		m_d3dPP.BackBufferHeight = 400;

		SetWindowLongPtr(m_hWnd, GWL_STYLE, WS_OVERLAPPEDWINDOW);
		SetWindowPos
		(
			m_hWnd, 
			HWND_TOP, 
			(GetSystemMetrics(SM_CXSCREEN) - rect.right + rect.left)/2,
			(GetSystemMetrics(SM_CYSCREEN) - rect.bottom + rect.top)/2,
			rect.right, 
			rect.bottom,
			SWP_NOZORDER | SWP_SHOWWINDOW
		);
	}
	m_pCurrentState->onLostDevice();
	HR(g_pD3dDev->Reset(&m_d3dPP));
	m_pCurrentState->onResetDevice();
}

int App::run()
{
	MSG msg;
	msg.message = WM_NULL;
	while(msg.message != WM_QUIT)
	{
		if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
		else if (isDevReady())
		{
			m_pCurrentState->update(1.0f);
			m_pCurrentState->draw();
		}
	}
	return msg.wParam;
}

void App::setCurrentState(State * p_pState)
{
	m_pCurrentState = p_pState;
}
State * App::getCurrentState()
{
	return m_pCurrentState;
}
